package com.makemoney.miniprogram.common.handler;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import com.makemoney.miniprogram.common.result.CommonResult;
import com.makemoney.miniprogram.common.result.BusinessException;
import com.makemoney.miniprogram.common.result.ResultCode;
import lombok.extern.slf4j.Slf4j;
import org.springframework.core.annotation.Order;
import org.springframework.validation.BindException;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.validation.ObjectError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;

import java.util.List;
import java.util.StringJoiner;

@Slf4j
@RestControllerAdvice
public class GlobalExceptionHandler {

    @ExceptionHandler(value = MethodArgumentNotValidException.class)
    public CommonResult<?> handle(MethodArgumentNotValidException exception) {
        BindingResult bindingResult = exception.getBindingResult();
        StringJoiner joiner = new StringJoiner(StrUtil.COLON);
        for (ObjectError error : bindingResult.getAllErrors()) {
            String code = error.getCode();
            String[] codes = error.getCodes();
            String property = codes[1];
            property = property.replace(code, StrUtil.EMPTY).replaceFirst(StrUtil.DOT, StrUtil.EMPTY);
            String defaultMessage = error.getDefaultMessage();
            joiner.add(property + defaultMessage);
        }
        return CommonResult.failed(ResultCode.A0400, joiner.toString());
    }

    @ExceptionHandler(value = BindException.class)
    public CommonResult<?> handle(BindException exception) {
        BindingResult result = exception.getBindingResult();
        String message = StrUtil.EMPTY;
        if (result.hasErrors()) {
            List<ObjectError> errors = result.getAllErrors();
            if (CollUtil.isNotEmpty(errors)) {
                errors.forEach(p -> {
                    FieldError fieldError = (FieldError) p;
                    log.error("Data check failure : object{" + fieldError.getObjectName() + "},field{" + fieldError.getField() +
                            "},errorMessage{" + fieldError.getDefaultMessage() + "}");
                });
                if (errors.size() > 0) {
                    FieldError fieldError = (FieldError) errors.get(0);
                    message = fieldError.getField() + fieldError.getDefaultMessage();
                }
            }
        }
        if (StrUtil.isEmpty(message)) {
            return CommonResult.failed(ResultCode.A0400);
        } else {
            return CommonResult.validateFailed(message);
        }
    }

    @ExceptionHandler(value = BusinessException.class)
    public CommonResult<?> handle(BusinessException exception) {
        return CommonResult.failed(exception.getErrorCode());
    }

    @ExceptionHandler(value = Exception.class)
    public CommonResult<?> handle(Exception exception) {
        StringBuilder message = new StringBuilder();
        message.append(exception.getMessage());
        message.append(StrUtil.CRLF);
        for (StackTraceElement stackTrace : exception.getStackTrace()) {
            message.append(stackTrace.getClassName() + "." + stackTrace.getMethodName() + ". at Line" + stackTrace.getLineNumber());
            message.append(StrUtil.CRLF);
        }
        log.error(message.toString());
        return CommonResult.failed(ResultCode.FAILED);
    }
}
